﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using QUT.Bio.Util;
using QUT.Bio.SilverMap.DefaultProvider;
using QUT.Bio.SilverMap.ObjectModel;
using QUT.Bio.SilverMap.MapService;

namespace QUT.Bio.SilverMap.DefaultProvider {
	public partial class Loader : UserControl {
		private PopupDialog dialog = new PopupDialog();
		private Canvas dialogContainer;
		private ObservableCollection<Gene> queryGenes = new ObservableCollection<Gene>();
		private List<Chromosome> queryChromosomes = new List<Chromosome>();
		private List<Chromosome> hitChromosomes = new List<Chromosome>();
		private ObjectModel model;
		private PopupOverlay overlay;
		
		private const int maxGenesDefault = 1000;

		public Loader () {
			InitializeComponent();
		}

		/// <summary>
		/// Initialises the load dialog, connected to the nominated model and view.
		/// </summary>
		/// <param name="model">The model that will be repopulated with loaded data.</param>
		/// <param name="overlay">A canvas capable of showing dialogs and alerts.</param>

		public void Init ( ObjectModel model, PopupOverlay overlay ) {
			this.model = model;
			this.overlay = overlay;

			dialog.Title = "Load BLAST hits";
			dialog.ClientArea.Centre = this;
			dialog.WindowChanged += dialog_WindowChanged;

			distanceChoiceField.ItemsSource = DataLayer.ListAvailableDistanceTypes();
			distanceChoiceField.SelectedItem = model.DistanceType.ToString();

			maxGenesField.Text = maxGenesDefault.ToString();
			
			thresholdTextField.Text = model.Cutoff.ToString();

			if ( model.Chromosomes == null ) {
				model.ChromosomesLoaded += RefreshSequenceLists;
			}
			else {
				RefreshSequenceLists();
			}

			// Prevent spurious drag events on the containing window when user sorts by columns
			MouseLeftButtonDown += delegate( object sender, MouseButtonEventArgs args ) {
				args.Handled = true;
			};

			queryGeneDataGrid.ItemsSource = queryGenes;

			if ( model.TagsEnabled ) {
				tagOperatorPanel.Visibility = Visibility.Visible;
				tagField.Visibility = Visibility.Visible;
				editTagsButton.Visibility = Visibility.Visible;
			}
			else {
				tagOperatorPanel.Visibility = Visibility.Collapsed;
				tagField.Visibility = Visibility.Collapsed;
				editTagsButton.Visibility = Visibility.Collapsed;
			}
		}

		/// <summary>
		/// Gets a reference to the popup dialog used to display this loader.
		/// </summary>
		public PopupDialog Dialog {
			get {
				return dialog;
			}
		}

		/// <summary>
		/// Get or set the the list of sequences from which candidate reference genes will be queried.
		/// <para>Accessor: gets a reference to the list of query sequences.</para>
		/// <para>Mutator: replaces the contents of the list with the contents of the supplied value.</para>
		/// </summary>
		public IEnumerable<Chromosome> QuerySequences {
			get {
				return queryChromosomes;
			}
			set {
				if ( value != queryChromosomes ) {
					queryChromosomes.Clear();
					queryChromosomes.AddRange( value );
					querySequenceField.Text = value.Join( "," );
				}
			}
		}

		/// <summary>
		/// Get or set the the list of sequences from which hits will be queried.
		/// <para>Accessor: gets a reference to the list of hit sequences.</para>
		/// <para>Mutator: replaces the contents of the list with the contents of the supplied value.</para>
		/// </summary>
		public IEnumerable<Chromosome> HitSequences {
			get {
				return hitChromosomes;
			}
			set {
				if ( value != hitChromosomes ) {
					hitChromosomes.Clear();
					hitChromosomes.AddRange( value );
					hitSequenceField.Text = value.Join( "," );
				}
			}
		}

		private void RefreshSequenceLists () {
			querySequenceField.Text = queryChromosomes.Join( "," );
			hitSequenceField.Text = hitChromosomes.Join( "," );
		}

		bool initialising = true;

		private void dialog_WindowChanged ( PopupWindow source, WindowChangeEventType eventType ) {
			if ( eventType == WindowChangeEventType.WindowShown ) {
				if ( initialising ) {
					initialising = false;
					dialogContainer = dialog.Container;

					if ( model.ReferenceSequence != null && model.Chromosomes != null ) {
						queryGenes.Clear();
						queryGenes.Add( (Gene) model.ReferenceSequence );
						QuerySequences = new Chromosome[] { ( (Gene) model.ReferenceSequence ).Chromosome };
						HitSequences = model.Subjects;
					}
				}
			}
			if ( eventType == WindowChangeEventType.WindowClosed ) {
				if ( dialog.Action == DialogAction.Ok ) {
					if ( queryGeneDataGrid.SelectedItem == null ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Please select a query gene." );
						return;
					}

					bool subjectSelected = this.hitChromosomes.Count > 0;

					if ( !subjectSelected ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Please select one or more subject\nsequences for comparison with the query." );
						return;
					}

					model.Subjects.Clear();
					model.Subjects.AddRange( hitChromosomes );

					double cutoff;

					if ( !double.TryParse( thresholdTextField.Text.Trim(), out cutoff ) ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Invalid numeric data for Threshold." );
						return;
					}

					if ( cutoff <= 1e-300 ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Threshold should be greater than 1e-300." );
						return;
					}

					int maxHits;
					
					if ( ! int.TryParse( this.maxHitsTextField.Text.Trim(), out maxHits ) ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Invalid numeric data for Max Hits." );
						return;
					}

					if ( maxHits <= 0 ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Max Hits should be at least 1." );
						return;
					}
					
					if ( maxHits > 100 ) {
						dialog.Container = dialogContainer;
						Alert.Show( "Max Hits may not exceed 100 in the present version." );
						return;
					}

					model.Load( 
						hitChromosomes, 
						(Gene) queryGeneDataGrid.SelectedItem,
						(DistanceType) Enum.Parse( typeof( DistanceType ), (string) distanceChoiceField.SelectedItem, false ),
						cutoff,
						maxHits
					);
				}
			}
		}

		private void searchButton_Click ( object sender, RoutedEventArgs e ) {
			int maxGenes = maxGenesDefault;
			
			if ( ! int.TryParse( maxGenesField.Text.Trim(), out maxGenes ) ) {
				Alert.Show( "Maximum genes must be numeric." );
				return;
			}
			
			if ( maxGenes <= 0 ) {
				Alert.Show( "Maximum genes must be greater than 0." );
				return;
			}

			queryGenes.Clear();

			//var taggedGeneIds = from rdfTag in Tags.AllTagsAccessibleToUser
			//                    from tagTemplate in tags
			//                    where Tags.NamespaceOf( rdfTag.TagSubjectUri ) == "mquter_map_gene"
			//                    && tagTemplate.Matches( rdfTag.TagLabelName, rdfTag.TagPredicateUri, rdfTag.TagObjectUri )
			//                    select ToDecimal( Tags.IdOf( rdfTag.TagSubjectUri ) );

			Chromosome queryOrganism = queryChromosomes.Count > 0 ? queryChromosomes[0] : null;
			string queryString = searchTextField.Text.Trim();

			bool loadingTaggedGenes = false; // taggedGeneIds.Count() > 0 && (bool) orTagsButton.IsChecked;
			bool loadingQueryGenes = queryOrganism != null && queryString.Length != 0;
			int loadingCounter = ( loadingTaggedGenes ? 1 : 0 ) + ( loadingQueryGenes ? 1 : 0 );

			if ( loadingCounter > 0 ) {
				searchButton.IsEnabled = false;

				Action enableSearchButton = delegate() {
					lock ( searchButton ) { // Lock on _something_
						if ( loadingCounter > 0 ) {
							loadingCounter--;
							searchButton.IsEnabled = loadingCounter == 0;

							if ( loadingCounter == 0 && queryGenes.Count == 0 ) {
								Alert.Show( "No genes matched your search criteria." );
							}
						}
					}
				};

				if ( loadingTaggedGenes && model.dataLayer != null ) {
					//model.dataLayer.GetBasicGeneInfo(
					//    taggedGeneIds,
					//    delegate( IEnumerable<Gene> genes ) {
					//        queryGenes.AddRange( genes );
					//        enableSearchButton();
					//    }
					//);
				}

				if ( loadingQueryGenes && model.dataLayer != null ) {
					model.dataLayer.ListGenes(
						from sequence in queryChromosomes
						select sequence.Accession,
						searchTextField.Text.Trim(),
						maxGenes,
						delegate( Gene gene ) {
							queryGenes.Add( gene );
						},
						delegate() {
							enableSearchButton();
						}
					);
				}
			}
			else {
				Alert.Show( "No genes matched your search criteria." );
			}
		}

		private void EditTagsButtonClicked ( object sender, RoutedEventArgs e ) {
			//ObjectTagChooser tagChooser = null;

			//tagChooser = new ObjectTagChooser() {
			//    SelectedTags = tags,
			//    WhenOkClicked = delegate() {
			//        tags.Clear();
			//        tags.AddRange( tagChooser.SelectedTags );
			//        tagField.Text = tags.Join( ", " );
			//    }
			//};

			//overlay.ShowWindow( tagChooser.Dialog, 100, 100, 640, 480 );
		}

		/// <summary>
		/// (Tries to) convert a string to a decimal numeric value.
		/// </summary>
		/// <param name="s">The text to parse.</param>
		/// <returns>A decimal representation of the string, or decimal.MinValue if the string is not a valid decimal.</returns>

		public static decimal ToDecimal ( string s ) {
			try {
				return decimal.Parse( s );
			}
			catch {
				return decimal.MinValue;
			}
		}

		private void EditSequenceButtonClicked ( object sender, RoutedEventArgs e ) {
			bool selectingHitChromosomes = sender == editHitSequenceButton;

			TextBox textBox = selectingHitChromosomes ? hitSequenceField : querySequenceField;
			List<Chromosome> selectionList = selectingHitChromosomes ? hitChromosomes : queryChromosomes;

			SequenceChooser chooser = null;

			chooser = new SequenceChooser( model ) {
				SelectedChromosomes = selectionList,
				WhenOkClicked = delegate() {
					selectionList.Clear();
					selectionList.AddRange( chooser.SelectedChromosomes );
					textBox.Text = selectionList.Join( ", " );
				}
			};

			overlay.ShowWindow( chooser.Dialog,
				overlay.ActualWidth / 8 - 20,
				overlay.ActualHeight / 8 + 20,
				overlay.ActualWidth * 0.6,
				overlay.ActualHeight * 0.6
			);
		}
	}
}
